package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author yb0os1
 * @since 2024-12-04
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {
    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper learningRecordMapper;

    /**
     * 添加用户课程
     *
     * @param userId    用户id
     * @param courseIds 课程id集合
     */
    @Override
    @Transactional
    public void addUserLesson(Long userId, List<Long> courseIds) {
        //在learning_lesson表中 需要添加userid courseid expiretime 其他走默认即可
        //1、根据课程id查询课程信息
        List<CourseSimpleInfoDTO> courseInfos = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(courseInfos)) {
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        //2、遍历课程信息 创建learning_lesson对象 并且放到集合中
        List<LearningLesson> learningLessons = new ArrayList<>();
        for (CourseSimpleInfoDTO courseInfo : courseInfos) {
            LearningLesson learningLesson = new LearningLesson();
            learningLesson.setUserId(userId);
            learningLesson.setCourseId(courseInfo.getId());
            Integer validDuration = courseInfo.getValidDuration();
            if (validDuration != null) {
                LocalDateTime now = LocalDateTime.now();
                learningLesson.setExpireTime(now.plusMonths(validDuration));
                learningLesson.setCreateTime(now);
            }
            learningLessons.add(learningLesson);
        }
        //3、批量插入
        this.saveBatch(learningLessons);
        log.info("添加课程成功");
    }

    /**
     * 分页查询我的课表
     *
     * @param page
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessonPage(PageQuery page) {
        //1、先获取当前用户
        Long userId = UserContext.getUser();
        if (userId == null) {
            //没用登录
            throw new BadRequestException("用户未登录");
        }
        //2、分页查询我的课表
        Page<LearningLesson> pageRes = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .page(page.toMpPage("latest_learn_time", false));//这是默认的  前端有传来的话就按照前端传来的内容进行排序
        List<LearningLesson> records = pageRes.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(pageRes);
        }
        //3、远程调用课表服务 给vo中的课程名 封面 章节数 赋值  因为po和vo里面具体的字段是有差异的
        //获取课程的id 根据课程的id获取细节内容
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        List<CourseSimpleInfoDTO> infoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(infoList)) {
            throw new BizIllegalException("课程不存在");
        }
        //4、将po的数据封装到vo
        //key为courseId value为所对应的课程信息对象
        Map<Long, CourseSimpleInfoDTO> infoDTOMap = infoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        //遍历po
        //定义用来收集结果的机车
        List<LearningLessonVO> res = new ArrayList<>();
        for (LearningLesson record : records) {
            //获取po所对应的vo
            CourseSimpleInfoDTO simpleInfoDTO = infoDTOMap.get(record.getCourseId());
            LearningLessonVO learningLessonVO = BeanUtils.copyBean(record, LearningLessonVO.class);
            if (simpleInfoDTO != null) {
                learningLessonVO.setCourseName(simpleInfoDTO.getName());
                learningLessonVO.setCourseCoverUrl(simpleInfoDTO.getCoverUrl());
                learningLessonVO.setSections(simpleInfoDTO.getSectionNum());
            }
            res.add(learningLessonVO);
        }
        //5、返回
        return PageDTO.of(pageRes, res);
    }

    /**
     * 查询当前用户正在学习的课程
     *
     * @return
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        //1、获取当前用户
        Long user = UserContext.getUser();
        //其实属于是多余的验证 在拦截器已经验证一次了
        if (user == null) {
            throw new BadRequestException("用户未登录");
        }

        //2、查询最近的正在学习的课程
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")//只能取最近的一 所以拼接一下
                .one();
        if (lesson == null) {
            log.error("当前用户最近没有学习课程");
            return null;
        }

        //3、远程调用课表服务 给vo中的课程名 封面 章节数 赋值
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (courseInfo == null) {
            throw new BizIllegalException("课程不存在");
        }

        //4、查询当前用户课表中 已报名总的课程数
        Integer count = lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .count();

        //5、远程调用小结服务 给vo中的小节名 小节编号 赋值
        //根据 目录id列表 查询目录信息
        List<Long> latestSectionIdList = CollUtils.singletonList(lesson.getLatestSectionId());
        List<CataSimpleInfoDTO> cataSimpleInfoDTOList = catalogueClient.batchQueryCatalogue(latestSectionIdList);
        if (CollUtils.isEmpty(cataSimpleInfoDTOList)) {
            throw new BizIllegalException("小节不存在");
        }

        //6、封装vo返回
        //需要填充的字段：封面 章节数 课程名   已报名总的课程数  小节编号 小节名
        LearningLessonVO learningLessonVO = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        learningLessonVO.setCourseName(courseInfo.getName());
        learningLessonVO.setCourseCoverUrl(courseInfo.getCoverUrl());
        learningLessonVO.setSections(courseInfo.getSectionNum());
        learningLessonVO.setCourseAmount(count);
        learningLessonVO.setLatestSectionName(cataSimpleInfoDTOList.get(0).getName());
        learningLessonVO.setLatestSectionIndex(cataSimpleInfoDTOList.get(0).getCIndex());

        return learningLessonVO;
    }

    @Override
    //TODO 不清楚当课程过期的时候是返回null还是lessonId  目前我实现的就是过期也返回null
    public Long checkIsValidLesson(Long courseId) {
        //1、获取当前用户
        Long user = UserContext.getUser();
        if (courseId == null) {
            throw new BizIllegalException("参数为传递或错误");
        }
        //2、根据课程id和用户id来获取对应的lesson对象
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        //3、检查lesson对象是否为空
        //4、检查课程过期时间
        if (lesson == null
                || (lesson.getExpireTime() != null && lesson.getExpireTime().isBefore(LocalDateTime.now()))
                || lesson.getStatus().equals(3)) {//用户没有报名
            return null;
        }
        return lesson.getId();
    }

    /**
     * 根据课程id，查询当前用户的课表中是否有该课程，如果有该课程则需要返回课程的学习进度、课程有效期等信息
     *
     * @param courseId 课程id
     * @return
     */
    @Override
    public LearningLessonVO queryLessonStatus(Long courseId) {
        //1、获取当前用户
        Long user = UserContext.getUser();
        if (courseId == null) {
            throw new BizIllegalException("参数为传递或错误");
        }
        //2、根据课程id和用户id查询课表
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        //3、有该课程
        if (lesson != null) {//用户没有报名
            return BeanUtils.copyBean(lesson, LearningLessonVO.class);
        }
        return null;
    }

    //统计课程报名人数
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        if (courseId == null) {
            throw new BizIllegalException("参数为传递或错误");
        }
        return this.lambdaQuery().eq(LearningLesson::getCourseId, courseId).count();
    }
    //删除失效的课程

    @Override
    @Transactional
    public void deleteLesson(Long courseId) {
        //1、获取当前用户
        Long user = UserContext.getUser();
        //2、查询课程
        LearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId, user)
                .eq(LearningLesson::getCourseId, courseId)
                .one();
        if (lesson == null) {
            throw new BizIllegalException("课程不存在");
        }
        if (lesson.getStatus().equals(3)) {
            //课程失效 可以删除
            boolean b = removeById(lesson.getId());
            if (!b) {
                throw new DbException("删除失败");
            }
            log.info("失效课程{}被删除了", courseId);
        }
        log.error("课程还没有失效，不可以删除");
    }

    /**
     * 创建学习计划
     * @param dto
     */
    @Override
    @Transactional
    public void createLearningPlan(LearningPlanDTO dto) {
        //1、获取当前用户
        Long userId = UserContext.getUser();
        //2、查询课程 需要进行一个判断：该课程是否存在
        LearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, dto.getCourseId())
                .one();

        if (lesson==null){
            throw new BizIllegalException("课程不存在");
        }
        //3、修改学习计划 状态 频率
        boolean success = this.lambdaUpdate()
                .set(lesson.getPlanStatus().equals(PlanStatus.NO_PLAN.getValue()),LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING.getValue())
                .set(LearningLesson::getWeekFreq, dto.getFreq())
                .eq(LearningLesson::getId, lesson.getId())
                .update();
        if (!success){
            log.error("修改学习计划失败");
            throw new DbException("修改学习计划失败");
        }
    }

    /**
     * 查询学习计划
     * @param page 分页参数
     * @return 查看的结果
     */

    @Override
    public LearningPlanPageVO queryLearningPlan(PageQuery page) {
        //1、获取当前用户
        Long userId = UserContext.getUser();

        //2、todo 本周学习积分暂时没有做

        //3、获取每一个课程的本周计划
        /*
         * 需要的字段有：
         * 课程id 课程名（需要远程调用课程微服务） 本周已学习数量（record）
         * 本周计划学习量 总已学习量 总小节数量（需要远程调用课程微服务） 最近学习时间
         *
         * 查询的条件 user_id = ? and plan_status = ? and status in (?,?) 课程要求没有过期并且没有学完
         */
        Page<LearningLesson> lessonPage = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING.getValue())
                .in(LearningLesson::getStatus, Arrays.asList(LessonStatus.NOT_BEGIN, LessonStatus.LEARNING))
                .page(page.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = lessonPage.getRecords();
        if (CollUtils.isEmpty(records)){
            //没有一个课程是做了学习计划的 返回一个空的vo
            LearningPlanPageVO vo = new LearningPlanPageVO();
            return vo.pageInfo(0L, 0L, CollUtils.emptyList());
        }

        //存在课程有本周学习计划
        //4、需要远程调用courseClient 获取课程名 总小节数量  传入课程idList
        //获取课程ids
        List<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
        List<CourseSimpleInfoDTO> courseSimpleInfos = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(courseSimpleInfos)){
            throw new BizIllegalException("课程不存在");
        }
        Map<Long, CourseSimpleInfoDTO> map = courseSimpleInfos.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        //遍历learningLessons也就是存在本周计划的课程
        //先声明一个vo的list
        List<LearningPlanVO> list = new ArrayList<>();
        //定义本周计划总数 和 本周完成总数
        Integer weekTotalPlan = 0,weekFinished = 0;
        for (LearningLesson learningLesson : records) {
            //获取对应的课程信息
            CourseSimpleInfoDTO courseSimpleInfoDTO = map.get(learningLesson.getCourseId());
            if (courseSimpleInfoDTO==null){
                continue;
            }
            LearningPlanVO learningPlanVO = BeanUtils.copyBean(learningLesson, LearningPlanVO.class);
            /*
                课程名称 课程章节数量 本周已学习章节数
             */
            learningPlanVO.setCourseName(courseSimpleInfoDTO.getName());
            learningPlanVO.setSections(courseSimpleInfoDTO.getSectionNum());
            //5、根据lesson_id获取本周学习的小节数量
            LocalDate now = LocalDate.now();
            LocalDateTime weekBeginTime = DateUtils.getWeekBeginTime(now);
            LocalDateTime weekEndTime = DateUtils.getWeekEndTime(now);
            //select count(*) from learning_record where finished = 1 and lesson_id = #{id} and finish_time between #{weekBeginTime} and #{weekEndTime}
            Integer learnedSections = learningRecordMapper.getInWeekLearnedSections(learningLesson.getId(), weekBeginTime, weekEndTime);
            learningPlanVO.setWeekLearnedSections(learnedSections);
            list.add(learningPlanVO);
            //获取本周计划总数 本周完成总数
            weekTotalPlan += learningLesson.getWeekFreq();
            weekFinished += learnedSections;
        }
        LearningPlanPageVO vo = new LearningPlanPageVO();
        //放入本周学习计划总数 本周完成总数
        vo.setWeekFinished(weekFinished);
        vo.setWeekTotalPlan(weekTotalPlan);
        return vo.pageInfo(lessonPage.getTotal(), lessonPage.getPages(), list);
    }

}
